﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using WTwitter.Model.Utility;

namespace WTwitter.Model.Framework.TextSplitter {
	sealed class Splitter {

		#region private member
		private static SplitStep _userNameSplitter;
		private static SplitStep _urlSplitter;
		private static SplitStep _tagSplitter;
		#endregion

		static Splitter() {
			_userNameSplitter = new SplitStep(RegexPatterns.UserName, text => new UserName(text));
			_urlSplitter = new SplitStep(RegexPatterns.UrlPattern, text => new Url(text));
			_tagSplitter = new SplitStep(RegexPatterns.TagPattern, text => new Tag(text));
		}

		/// <summary>
		/// 文字列をURLやIDごとに分割する
		/// </summary>
		/// <param name="text">元となる文字列</param>
		/// <returns>分割後のオブジェクト</returns>
		static public List<TextComponent> Split(string text) {
			//最初は１固まりのテキストから始めて
			//前段の分割結果（リスト）を後段の入力とする
			return 
				_tagSplitter.Split(
					_urlSplitter.Split(
						_userNameSplitter.Split(
							new TextComponent[] {new PlainText(text)}
							)))
					.ToList<TextComponent>()	;
		}

		/// <summary>
		/// 分割の１ステップを表すクラス（１つの正規表現に対する一致を探して分割）
		/// </summary>
		private class SplitStep {
			private Regex _regex;
			private string _pattern;
			private Func<string, TextComponent> _generator;

			/// <summary>
			/// コンストラクタ
			/// </summary>
			/// <param name="pattern">検出するための正規表現</param>
			/// <param name="generator">検出した文字列から新しいTextComponentを生成する関数</param>
			public SplitStep(string pattern, Func<string, TextComponent> generator) {
				_pattern = pattern;
				_regex = new Regex(pattern, RegexOptions.Compiled);
				_generator = generator;
			}

			/// <summary>
			/// 正規表現に一致する部分を見つけて、分割した結果を返す
			/// IEnumerableの各アイテムごとに
			/// a. 入力がプレーンテキストの場合
			/// 　　正規表現に一致する部分とその前後の３つに分割して、３つのアイテムを返す
			/// 　　前後はプレーンテキストとして、一致部分は検出したタイプのクラスとして返す
			/// 　　※先頭に一致した場合、２カ所に一致した場合などは返す個数が異なる
			/// b. 入力がプレーンテキスト以外（前段ですでに検出されたタイプ）の場合
			/// 　　そのまま返す
			/// </summary>
			/// <param name="source"></param>
			/// <returns></returns>
			public IEnumerable<TextComponent> Split(IEnumerable<TextComponent> source) {
				foreach (var item in source) {
					if (item.Type != TextComponentType.Plain) {
						//プレーンテキスト以外はそのまま返す
						yield return item;
					} else {
						var matches = _regex.Matches(item.Text);
						if (matches.Count > 0) {
							var index = 0;
							foreach (Match match in matches) {
								if (index < match.Index) {
									//一致より前の部分
									yield return new PlainText(item.Text.Substring(index, match.Index - index));
								}
								//一致した部分
								yield return _generator(match.Value);
								index = match.Index + match.Length;
							}
							if (index < item.Text.Length) {
								//一致より後ろの部分
								yield return new PlainText(item.Text.Substring(index));
							}
						} else {
							//一致がなかったらそのまま返す
							yield return item;
						}
					}
				}
			}
		}

	}
}
